const axios = require('axios');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const { pipeline } = require('stream'); // 引入流式处理工具

// 配置参数
const SERVER_URL = 'http://localhost:8080/download'; // 服务端地址
const OUTPUT_FILE = 'downloaded_file.zip';  // 最终文件名
const TEMP_DIR = 'temp';                    // 临时文件夹
const CHUNK_SIZE = 100 * 1024 * 1024;       // 每个分片大小（100MB）
const MAX_CONCURRENT = 5;                   // 最大并发请求数

// 重试机制配置
const MAX_RETRIES = 3;                     // 最大重试次数
const RETRY_DELAY = 1000;                  // 重试间隔（毫秒）

// 异步文件操作的 Promise 化
const mkdirAsync = promisify(fs.mkdir);
const statAsync = promisify(fs.stat);
const readdirAsync = promisify(fs.readdir);
const unlinkAsync = promisify(fs.unlink);

async function downloadFile() {
  try {
    // 1. 创建临时文件夹
    await mkdirAsync(TEMP_DIR, { recursive: true });

    // 2. 获取文件总大小
    const fileSize = await getFileSize();
    console.log(`文件总大小: ${fileSize} bytes`);

    // 3. 计算分片范围
    const chunks = calculateChunks(fileSize);
    console.log(`分片数量: ${chunks.length}`);

    // 4. 并行下载分片到临时文件夹
    await downloadChunks(chunks);

    // 5. 合并所有分片到最终文件
    await mergeChunks();

    // 6. 清理临时文件
    await cleanupTemp();

    console.log('下载完成');
  } catch (error) {
    console.error('下载失败:', error.message);
    throw error;
  }
}

// 获取文件总大小
async function getFileSize() {
  const headResponse = await axios.head(SERVER_URL);
  const contentLength = headResponse.headers['content-length'];
  if (!contentLength) throw new Error('无法获取文件大小');
  return parseInt(contentLength, 10);
}

// 计算分片范围
function calculateChunks(totalSize) {
  const chunks = [];
  let start = 0;
  while (start < totalSize) {
    const end = Math.min(start + CHUNK_SIZE - 1, totalSize - 1);
    chunks.push({ start, end });
    start = end + 1;
  }
  return chunks;
}

// 下载分片到临时文件夹（流式优化）
async function downloadChunks(chunks) {
  let index = 0;
  const promises = [];

  while (index < chunks.length) {
    const nextChunk = chunks[index++];
    promises.push(downloadChunk(nextChunk));
    if (promises.length >= MAX_CONCURRENT) {
      await Promise.all(promises);
      promises.length = 0;
    }
  }
  if (promises.length > 0) {
    await Promise.all(promises);
  }
}

// 下载单个分片（流式写入）
async function downloadChunk({ start, end }) {
  const chunkId = `${start}-${end}`;
  const tempFilePath = path.join(TEMP_DIR, `chunk_${chunkId}`);

  const retry = (attempt) => {
    return new Promise(async (resolve, reject) => {
      try {
        // 使用流式下载
        const response = await axios.get(SERVER_URL, {
          responseType: 'stream', // 关键：启用流式响应
          headers: { Range: `bytes=${start}-${end}` },
          timeout: 5000 // 超时时间
        });

        // 将响应流写入文件（使用 pipeline）
        await pipeline(
          response.data, // axios 的可读流
          fs.createWriteStream(tempFilePath),
          (err) => {
            if (err) throw err;
            console.log(`流式下载分片成功: ${chunkId}`);
            resolve();
          }
        );
      } catch (error) {
        // 关闭流并释放资源
        if (response?.data?.destroy) response.data.destroy();
        if (attempt < MAX_RETRIES) {
          console.log(`重试分片 ${chunkId}（剩余尝试: ${MAX_RETRIES - attempt}）`);
          setTimeout(() => retry(attempt + 1), RETRY_DELAY);
        } else {
          reject(new Error(`分片下载失败: ${chunkId}`));
        }
      }
    });
  };
  return retry(0);
}

// 合并所有分片到最终文件（流式优化）
async function mergeChunks() {
  // 1. 获取所有临时文件列表并排序
  const files = await readdirAsync(TEMP_DIR);
  const sortedFiles = files
    .filter(file => file.startsWith('chunk_'))
    .sort((a, b) => {
      const startA = parseInt(a.split('_')[1].split('-')[0], 10);
      const startB = parseInt(b.split('_')[1].split('-')[0], 10);
      return startA - startB;
    });

  // 2. 创建最终文件的可写流
  const finalWriteStream = fs.createWriteStream(OUTPUT_FILE);

  // 3. 逐个读取分片流并写入最终文件
  for (const file of sortedFiles) {
    const tempPath = path.join(TEMP_DIR, file);
    const readStream = fs.createReadStream(tempPath);
    await new Promise((resolve, reject) => {
      readStream.pipe(finalWriteStream);
      readStream.on('end', resolve);
      readStream.on('error', reject);
    });
    console.log(`合并分片: ${file}`);
  }
}

// 清理临时文件
async function cleanupTemp() {
  const files = await readdirAsync(TEMP_DIR);
  for (const file of files) {
    const filePath = path.join(TEMP_DIR, file);
    await unlinkAsync(filePath);
  }
}

// 导出下载函数
module.exports = { downloadFile };